#include "BangoLogger.h"

BangoLogger::BangoLogger()
{
    _logEvents = BangoConstants::SETTING_LOGEVENTS;
    _sessionResumePeriod = BangoConstants::SETTING_SESSION_RESUME_PERIOD;
    _postURL = BangoConstants::SETTING_POST_URL;
    _useOfflineStorage = BangoConstants::SETTING_USEOFFLINESTORAGE;
    _findLocation = BangoConstants::SETTING_FINDLOCATION;
    _applicationType = "";
    _applicationChannel = "";
    _locationSource = BangoConstants::SETTING_LOCATION_SOURCE;

}

BangoLogger::~BangoLogger()
{
    ClearEvents();
}

void BangoLogger::GetLoggerProperties()
{
    _networkName = BangoConstants::UNKNOWN_INFORMATION;
    _networkCountry = BangoConstants::UNKNOWN_INFORMATION;
    _networkId = BangoConstants::UNKNOWN_INFORMATION;
    _isRoaming = false;

    QSystemNetworkInfo * networkInfo = new QSystemNetworkInfo();

    if (networkInfo)
    {
        _networkName = networkInfo->networkName(QSystemNetworkInfo::GsmMode);
        _networkCountry = networkInfo->currentMobileCountryCode(); //BangoStatic::GetCountryISO(networkInfo->currentMobileCountryCode());
        _networkId = networkInfo->currentMobileNetworkCode();
        _isRoaming = (networkInfo->networkStatus(QSystemNetworkInfo::GsmMode) == QSystemNetworkInfo::Roaming);
    }
}

void BangoLogger::ClearEvents()
{    
    QVectorIterator<BangoEvent*> iter(_bangoEvents);

    // Not sure if deleting items affects what the next item will be,
    // so deleting items from back to front.
    iter.toBack();
    while (iter.hasPrevious())
    {
        delete iter.previous();
    }

    _bangoEvents.clear();
}

void BangoLogger::InitLogger(QString applicationID)
{
    _applicationId = applicationID;

    QString brand;
    QString device;
    QString model;
    QString product;
    QString version;
    QString buildid;


    QSystemDeviceInfo* deviceInfo = new QSystemDeviceInfo();

    if (deviceInfo)
    {
        brand = deviceInfo->manufacturer();
        device = deviceInfo->model();
        product = deviceInfo->productName();
        version = BangoStatic::GetOperatingSystemVersion();
    }

    if (_applicationVersion == "")
    {
        _applicationVersion = qApp->applicationVersion();
    }

    if (_userId == "")
    {
        _userId = BangoStatic::GetUserID();
    }

    if (_userAgent == "")
    {
        _userAgent = BangoStatic::GetUserAgent();
    }

    GetLoggerProperties();

    _bangoProfile.SetValues(BangoConstants::SDK_TYPE, BangoConstants::SDK_VERSION,
                  _applicationId, _applicationType, _applicationChannel, _applicationVersion, _userId, _sessionResumePeriod,
                  BangoStatic::GetConnectionType(), _isRoaming, _locationSource,
                  _networkId, _networkName, _networkCountry, _networkName,
                  brand, device, model, product, version, buildid,
                  BangoStatic::GetOperatorConnectionType());

    GetLocation();
}

int BangoLogger::LogEvent(QString eventName, int errorLevel, QString eventValue, QString eventDetail, QString errorId, QMap<QString,QString> eventParameters)
{
    int returnValue = -1;

    if (_logEvents)
    {
        QDateTime currentTime = QDateTime::currentDateTime();

        QString eventLocalTime = currentTime.toString(BangoConstants::DATETIME_FORMAT);
        QString eventTime = currentTime.toUTC().toString(BangoConstants::DATETIME_FORMAT);

        QVector<BangoNameValuePair> eventParametersVector;

        if (eventParameters.size() > 0)
        {
            QMapIterator<QString, QString>iter(eventParameters);

            while (iter.hasNext())
            {
                iter.next();
                eventParametersVector.append(BangoNameValuePair(iter.key(), iter.value()));
            }
        }

        _bangoEvents.append(new BangoEvent(_bangoEvents.size(),
                              BangoStatic::GetConnectionType(), BangoStatic::GetOperatorConnectionType(),
                              eventName, eventValue, eventDetail, eventTime, eventLocalTime,
                              errorLevel, errorId,
                              eventParametersVector));

        returnValue = _bangoEvents.size();
    }
    return returnValue;
}

int BangoLogger::LogEvent(QString eventName, QString eventValue, QString eventDetail, QMap<QString,QString> eventParameters)
{
    return LogEvent(eventName, BangoConstants::ERROR_NONE, eventValue, eventDetail, "", eventParameters);
}
int BangoLogger::LogEvent(QString eventName, QString eventValue, QString eventDetail)
{
    return LogEvent(eventName, BangoConstants::ERROR_NONE, eventValue, eventDetail, "",  QMap<QString,QString>());
}


int BangoLogger::LogEventStart(QString eventValue, QString eventDetail, QMap<QString,QString> eventParameters)
{
    return LogEvent(BangoSessionEvents::SESSION_START, eventValue, eventDetail, eventParameters);
}

int BangoLogger::LogEventEnd(QString eventValue, QString eventDetail, QMap<QString,QString> eventParameters)
{
    return LogEvent(BangoSessionEvents::SESSION_END, eventValue, eventDetail, eventParameters);
}

int BangoLogger::LogEventError(QString errorId, QString errorClass, QString message, QMap<QString,QString> eventParameters)
{
    return LogEvent(BangoSessionEvents::EVENT_ERROR, BangoConstants::ERROR_USER, errorClass, message, errorId, eventParameters);
}

int BangoLogger::LogInternalError(exception& ex, int errorLevel)
{
    return LogEvent("", errorLevel, ex.what(), "", "", QMap<QString, QString>());
}

int BangoLogger::LogInternalErrorCritical(exception& ex)
{
    return LogInternalError(ex, BangoConstants::ERROR_INTERNAL_CRITICAL);
}

int BangoLogger::LogInternalErrorWarning(exception& ex)
{
    return LogInternalError(ex, BangoConstants::ERROR_INTERNAL_WARNING);
}

QString BangoLogger::GetJSONString(BangoProfile bangoProfile, QVector<BangoEvent*> bangoEvents)
{
    Serializer serializer;
    QVariantMap postProfile = bangoProfile.GetProfileDetails();
    QVariantList postEvents;
    QString returnString;

    if (bangoEvents.size() > 0)
    {
        QVectorIterator<BangoEvent*> iter(bangoEvents);

        while (iter.hasNext())
        {
            BangoEvent* bEvent = (BangoEvent*) iter.next();
            if (!bEvent->EventPosted())
            {
                postEvents.append(bEvent->GetEventDetails());
            }
        }
    }

    postProfile.insert("Events", postEvents);

    serializer.setIndentMode(Serializer::IndentFull);

    returnString.append(serializer.serialize(postProfile));
    return returnString;
}

bool BangoLogger::PostEvents(QString jsonString)
{
    bool returnValue = false;
    QByteArray dataToSend(jsonString.toAscii());
    QNetworkRequest postRequest;
    QEventLoop loop;
    QNetworkAccessManager* qnam= new QNetworkAccessManager();
    QNetworkReply* reply;
    // QByteArray responseData;
    // QString responseMessage;
    int responseCode;

    postRequest.setUrl(QUrl(_postURL));
    if (UserAgent() != "")
    {
        postRequest.setRawHeader("User-Agent", UserAgent().toAscii());
    }

    postRequest.setHeader(QNetworkRequest::ContentLengthHeader, dataToSend.length());
    postRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    postRequest.setRawHeader("Connection", "close");

    reply = qnam->post(postRequest, dataToSend);
    QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));

    // Eventloop used to ensure synchronous request.
    loop.exec();

    // Get Response data, and Http response code
    // responseData = reply->readAll();
    // responseMessage.append(responseData);
    responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    if (responseCode == 200)
    {
        returnValue = true;
    }

    return returnValue;
}

int BangoLogger::SaveCurrentEvents()
{
    QMutexLocker locker(&savecurrentMutex);
    BangoStorage lBangoStore;
    int returnValue = -1;

    QVector<BangoEvent*> eventsToSave;

    if (UseOfflineStorage())
    {
        QVectorIterator<BangoEvent*> iter(_bangoEvents);

        while (iter.hasNext())
        {
            BangoEvent* bEvent = (BangoEvent*) iter.next();
            if (!bEvent->EventPosted())
            {
                eventsToSave.append(bEvent);
            }
        }

        returnValue = lBangoStore.SaveRecord(GetJSONString(_bangoProfile, eventsToSave));

        if (returnValue != -1)
        {
            iter = QVectorIterator<BangoEvent*>(eventsToSave);

            while (iter.hasNext())
            {
                ((BangoEvent*)iter.next())->EventPosted(true);
            }
        }
    }

    return returnValue;
}

void BangoLogger::PostStoredEvents()
{    
    QMutexLocker locker(&postsavedMutex);
    BangoStorage lBangoStore;
    QMap<int, QString> stringsToPost = lBangoStore.GetRecords();

    if (stringsToPost.size() > 0)
    {
        QMapIterator<int, QString> iter(stringsToPost);

        while (iter.hasNext())
        {
            iter.next();
            if (PostEvents(iter.value()))
            {
                lBangoStore.DeleteRecord(iter.key());
            }
        }
    }
}

void BangoLogger::PostCurrentEvents()
{    
    QMutexLocker locker(&postcurrentMutex);
    QVector<BangoEvent*> eventsToPost;
    QVectorIterator<BangoEvent*> iter(_bangoEvents);

    while (iter.hasNext())
    {
        BangoEvent* bEvent = (BangoEvent*) iter.next();
        if (!bEvent->EventPosted())
        {
            eventsToPost.append(bEvent);
        }
    }

    if (eventsToPost.size() > 0)
    {
        iter = QVectorIterator<BangoEvent*>(eventsToPost);

        if (PostEvents(GetJSONString(_bangoProfile, eventsToPost)))
        {
            while (iter.hasNext())
            {
                ((BangoEvent*)iter.next())->EventPosted(true);
            }
        }
    }
}

QString BangoLogger::ViewBatch()
{
    return GetJSONString(_bangoProfile, _bangoEvents);
}

void BangoLogger::GetLocation()
{
    if (_findLocation && !_locationDataSource)
    {
        _locationDataSource = QGeoPositionInfoSource::createDefaultSource(this);
        if (_locationSource == BangoConstants::POSITIONINGMETHOD_ALLPOSITIONINGMETHODS
                || _locationSource == BangoConstants::POSITIONINGMETHOD_NONSATELLITEPOSITION)
        {
            if (_locationSource == BangoConstants::POSITIONINGMETHOD_ALLPOSITIONINGMETHODS)
            {
                _locationDataSource->setPreferredPositioningMethods(QGeoPositionInfoSource::AllPositioningMethods);
            }
            else
            {
                _locationDataSource->setPreferredPositioningMethods(QGeoPositionInfoSource::NonSatellitePositioningMethods);
            }
        }
        else
        {
            _locationDataSource->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods);
        }

        QGeoPositionInfo geoPositionInfo = _locationDataSource->lastKnownPosition();
        if (geoPositionInfo.isValid())
        {
            _bangoProfile.UpdateLocationValues(geoPositionInfo.coordinate().altitude(),geoPositionInfo.coordinate().latitude(), geoPositionInfo.coordinate().longitude(), geoPositionInfo.attribute(QGeoPositionInfo::HorizontalAccuracy));
        }
        connect(_locationDataSource, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo)));
        _locationDataSource->startUpdates();

    }
}

void BangoLogger::positionUpdated(QGeoPositionInfo geoPositionInfo)
{

    if (geoPositionInfo.isValid())
    {
        _locationDataSource->stopUpdates();
        _bangoProfile.UpdateLocationValues(geoPositionInfo.coordinate().altitude(),geoPositionInfo.coordinate().latitude(), geoPositionInfo.coordinate().longitude(), geoPositionInfo.attribute(QGeoPositionInfo::HorizontalAccuracy));
    }
}


